#ifndef __open_vip_mem__
#define __open_vip_mem__

#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <string>
#include <map>
#include <functional>
#include <tuple>
#include "xspcomm/xutil.h"

namespace ovip {
// all adress is 64 bit
typedef uint64_t paddr_t;
typedef uint64_t reg_t;
typedef uint64_t word_t;
typedef reg_t addr_t;

typedef enum { endianness_little, endianness_big } endianness_t;

typedef std::function<void(paddr_t addr, size_t len, void *bytes)>
    copy_mem_func;

class SimpleMemory
{
public:
    typedef struct {
        paddr_t start;
        paddr_t end;
        u_int8_t *blk;
    } sp_mm_blk;

    unsigned block_size;
    std::map<paddr_t, u_int8_t *> mem;
    std::map<std::string, sp_mm_blk *> big_block;
    std::string name;
    u_char default_value = 0;

    ~SimpleMemory();
    SimpleMemory(std::string name, u_int block_count = 4,
                 u_int chunk_size = 1024, u_char default_value = 0) :
        name(name), default_value(default_value)
    {
        this->block_size = block_count * chunk_size;
        Debug("init SparseRam(%s) with block_size= %.2f kB (chunk_size=%d)",
              this->name.c_str(), float(this->block_size) / 1024.0, chunk_size);
    }

    bool LoadRaw(const char *file, paddr_t addr);
    bool LoadElf(const char *file);

    void Read(paddr_t addr, size_t len, void *bytes);
    void Write(paddr_t addr, size_t len, const void *bytes);
    bool AddBlk(char *name, paddr_t start, paddr_t end);
    void *BlkHostAddr(char *name);

    word_t Read(paddr_t addr, int len);
    void Write(paddr_t addr, int len, word_t data);
    void CloneTo(SimpleMemory *dst);

    endianness_t Endianness();

    void CopyToNDefaultValueBytes(copy_mem_func copy_handler);
    void CopyToBytes(copy_mem_func copy_handler);

    std::string InfoStr();

private:
    u_int max_cache_size = 5;
    std::map<paddr_t, std::tuple<u_int8_t *, u_long>> cache;
    bool _fast_count(paddr_t index);
    u_int8_t *_fast_mem(paddr_t index);
    void __update_cache(paddr_t index, u_int8_t *m);
    sp_mm_blk *_blk_find(paddr_t addr);
    bool _blk_read(paddr_t addr, size_t len, void *bytes);
    bool _blk_write(paddr_t addr, size_t len, const void *bytes);
};

} // namespace ovip

#endif
